﻿namespace LogManager.Console
{
    using LogManager.Core.Models;
    using LogManager.Core.Services.Location;
    using LogManager.Core.Services.Report;
    using LogManager.Utils.Threading;
    using System;
    using System.Threading;
    using LogManager.Core.Factories;

    public class Launcher
    {
        public const string XSD_PATH = "Config/schema.xsd";

        public readonly IConfiguration _configuration;
        private bool _hasRun;
        private readonly HierarchyPainter _painter;
        private readonly ConsoleSpinner _spinner;
        public readonly Options _options;

        public Launcher(Options options)
        {
            this._options = options;
            this._configuration = new DefaultXmlElementFactory().CreateConfiguration(options.ConfigurationPath, XSD_PATH);
            this._painter = new HierarchyPainter();
            this._spinner = new ConsoleSpinner();
            this._painter.Spinner = this._spinner;
        }

        public void Run()
        {
            if (this._hasRun)
            {
                throw new InvalidOperationException("Launcher has already run");
            }

            this._hasRun = true;
            this.RunInternal(this._configuration);
            this.DisplayEnd();
        }

        private void RunInternal(IConfiguration config)
        {
            this._painter.PaintStartingOptions(this._options);
            SynchronizationContext.SetSynchronizationContext(new SyncSynchronizationContext());

            switch (this._options.RunMode)
            {
                case RunMode.Archive:
                    this.RunArchiving(config);
                    break;
                case RunMode.Check:
                    this.RunCheck(config);
                    break;
            }
        }

        private void RunCheck(IConfiguration config)
        {
            var LocationArchivingProgress = new Progress<LocationArchivingProgress>(OnLocationArchivingProgress);
            LocationService.CheckLocationsAccess(
                        config,
                        new CancellationTokenSource().Token,
                        LocationArchivingProgress,
                        new Progress<object>(),
                        searchFiles: false);
        }

        private void RunArchiving(IConfiguration config)
        {
            var LocationArchivingProgress = new Progress<LocationArchivingProgress>(OnLocationArchivingProgress);
            var logFileProgress = new Progress<LogFileArchivingProgress>(OnLogFileArchivingProgress);

            var archivingResult = LocationService.LaunchArchiving(
                        config,
                        new CancellationTokenSource().Token,
                        LocationArchivingProgress,
                        new Progress<object>(),
                        logFileProgress);

            if (config.Settings.SmtpSettings.EnableNotifications)
            {
                this._spinner.Start();
                var mailingResult = ReportService.SendReportStatic(archivingResult);

                this._spinner.Stop();
                this._painter.PaintMailingResult(mailingResult);

            }
        }

        private void OnLocationArchivingProgress(LocationArchivingProgress progress)
        {
            Thread.Sleep(400);
            this._painter.Paint(progress.Location);
            switch (progress.LocationArchivingStep)
            {
                case LocationArchivingStep.CheckSource:
                    this._painter.PaintSource(progress);
                    break;
                case LocationArchivingStep.CheckDestination:
                    this._painter.PaintDestination(progress);
                    break;
                case LocationArchivingStep.CheckFiles:
                    this._painter.PaintFileResults(progress);
                    break;
            }
        }

        private void OnLogFileArchivingProgress(LogFileArchivingProgress archivingProgress)
        {
            this._painter.PaintFileArchiving(archivingProgress);
        }

        private void DisplayEnd()
        {
            this._painter.PaintEnd();
        }
    }
}